//
//  WatchLogging.swift
//  BMWOne
//
//  Created by Florian Goessler on 10/12/16.
//  Copyright © 2016 BMW. All rights reserved.
//

import Foundation
import Combine

#if DEVELOPMENT
private var WATCH_LOGGING_ENABLED = true
#else
private var WATCH_LOGGING_ENABLED = false
#endif

private var watchLogger: WatchLogger?

func WatchLog(_ logMsg: @autoclosure () -> String) {
    if WATCH_LOGGING_ENABLED {
        let msg = "[WATCH] \(logMsg())"
        NSLog(msg)

        if watchLogger == nil {
       //   watchLogger = ApplicationAssembly.resolve(WatchLogger.self)
        }
        watchLogger?.saveLogMessage((msg))
    }
}

class WatchLogger {

    private static let LOG_ROTATION_INTERVAL: TimeInterval = 48 * 60 * 60   // 2 days
    private static let LOG_TRANSFER_DATE = "LOG_TRANSFER_DATE"

    private let wcService: WCServiceProtocol
    private let fileManager: FileManagerProtocol

    private var fileHandle: FileHandle?
    private let loggingQueue: DispatchQueue = DispatchQueue(label: "WatchLogging")
    private var recieveFileCancelable: Set<AnyCancellable>

    lazy var logFilePath: String = {
        return NSHomeDirectory() + "/Documents/" + (self.isIOS() ? "WatchLog_iphone.txt" : "WatchLog_watch.txt")
    }()
    lazy var otherSideLogFilePath: String = {
        return NSHomeDirectory() + "/Documents/" + (self.isIOS() ? "WatchLog_watch.txt" : "WatchLog_iphone.txt")
    }()

    init(wcService: WCServiceProtocol, fileManager: FileManagerProtocol) {
        self.wcService = wcService
        self.fileManager = fileManager
        recieveFileCancelable = Set<AnyCancellable>()
        loggingQueue.async { [unowned self] in
            if let fileHandle = FileHandle(forWritingAtPath: self.logFilePath) {
                self.fileHandle = fileHandle
            } else {
                self.createLogFile()
            }
            self.fileHandle?.seekToEndOfFile()
        }

        if isIOS() {
            listenToLogsFromOtherSide()
        }
    }

    //MARK: - Public Methods
    func saveLogMessage(_ message: String) {
        guard WATCH_LOGGING_ENABLED else { return }

        loggingQueue.async { [weak self] in
            guard let `self` = self else { return }
            self.resetLogsIfNecessary()

            let contentToAppend = "[" + NSDate().description + "] " + message + "\n"
            guard let data = contentToAppend.data(using: .utf8) else { return }
            self.fileHandle?.write(data)
        }
    }

    func sendLogToOtherSide() {
        wcService.sendFile.send((WatchFile(
            url: URL(fileURLWithPath: self.logFilePath),
            metadata: [WatchLogger.LOG_TRANSFER_DATE: NSDate().timeIntervalSince1970]
        )))
    }
    
    //MARK: - Private Methods
    private func resetLogsIfNecessary() {
        var logCreationDate: AnyObject? = nil
        try? NSURL(fileURLWithPath: self.logFilePath).getResourceValue(&logCreationDate, forKey: URLResourceKey.creationDateKey)
        if let creationDate = logCreationDate as? Foundation.Date, Foundation.Date().timeIntervalSince(creationDate) > WatchLogger.LOG_ROTATION_INTERVAL {
            self.eraseLogFile()
            self.createLogFile()
        }
    }

    private func eraseLogFile() {
        _ = try? fileManager.removeItem(atPath: logFilePath)
    }

    private func createLogFile() {
        do {
            let str = "[\(Foundation.Date().description)] Starting watch log"
            try str.write(toFile: self.logFilePath, atomically: true, encoding: .utf8)
            self.fileHandle = FileHandle(forWritingAtPath: self.logFilePath)
        } catch {
            WatchLog("[WATCH-LOG] Error creating watch log at: \(self.logFilePath)")
            self.fileHandle = nil
        }
    }

    private func listenToLogsFromOtherSide() {
        wcService.receiveFile
            .filter({ return $0.metadata?[WatchLogger.LOG_TRANSFER_DATE] != nil })
            .sink { (_) in
                
            } receiveValue: { (file) in
                let destFilePath =  URL(fileURLWithPath: self.otherSideLogFilePath)
                NSLog("[WATCH-LOG] received watch log \(destFilePath)")
                _ = try? self.fileManager.removeItem(at: destFilePath)
                _ = try? self.fileManager.moveItem(at: file.url, to: destFilePath)
            }.store(in: &recieveFileCancelable)

    
    }
    private func isIOS() -> Bool {
        #if os(iOS)
            return true
        #else
            return false
        #endif
    }
    
    deinit {
        self.fileHandle?.closeFile()
    }

}
